package cn.flying.admin.controller.process;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;

import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.identity.Group;
import org.activiti.engine.impl.persistence.entity.GroupEntityImpl;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import cn.flying.base.common.constants.Constants;
import cn.flying.base.common.enums.RtCodeEnum;
import cn.flying.base.common.exception.BizException;
import cn.flying.base.common.utils.RandomUtils;
import cn.flying.base.common.utils.SnowflakeUtil;
import cn.flying.base.common.vo.PageRequest;
import cn.flying.base.common.vo.PageResponse;
import cn.flying.base.common.vo.Rt;
import cn.flying.base.core.common.utils.Ft;
import cn.flying.base.core.web.BaseController;
import cn.flying.base.process.bo.ProcDefinition;
import cn.flying.base.process.bo.ProcInstance;
import cn.flying.base.process.domain.cf.CfProcConfig;
import cn.flying.base.process.domain.cf.CfTaskPosition;
import cn.flying.base.process.enums.BizProcDefTypeEnum;
import cn.flying.base.process.service.ProcessDefinitionService;
import cn.flying.base.process.service.ProcessManageService;

/**
 * @description: 工作流处理器
 * @author: lvyong
 * @date: 2022年09月29日 20:11
 * @version: 1.0
 */
@RestController
@RequestMapping("/process")
@Validated
public class ProcessManageController extends BaseController {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private ProcessEngine processEngine;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private TaskService taskService;
    @Resource
    private IdentityService identityService;
    @Resource
    private ManagementService managementService;
    @Resource
    private HistoryService historyService;

    @Autowired
    private ProcessDefinitionService processDefinitionService;
    @Autowired
    private ProcessManageService processManageService;

    /**
     * 查询流程部署定义列表
     *
     * @param pageRequest
     * @return
     */
    @PostMapping("getProcessDefinitionPageList")
    public PageResponse getProcessDefinitionPageList(PageRequest<ProcDefinition> pageRequest) {
        String tenantId = (String) Ft.ThreadLocal.get(Constants.TENANT_ID);
        pageRequest.getParam().setTenantId(tenantId);
        PageResponse pageResponse = processDefinitionService.getProcessDefinitionPageList(pageRequest);
        return pageResponse;
    }

    /**
     * 部署流程定义
     *
     * @param file
     * @return
     * @throws Exception
     */
    @PostMapping("deployment/add")
    public Rt addDeployment(MultipartFile file) throws Exception {
        String tenantId = (String) Ft.ThreadLocal.get(Constants.TENANT_ID);
        Optional.ofNullable(tenantId).orElseThrow(() -> new BizException(RtCodeEnum.R10009));
        String deploymentId = processDefinitionService.deployment(file, "", "", tenantId);
        return Rt.success("流程部署成功！");
    }

    /**
     * 删除流程部署
     *
     * @param deploymentId
     * @param
     * @return
     */
    @PostMapping("deployment/del")
    public Rt deleteDeployment(@NotBlank(message = "流程部署ID不能为空") @RequestParam String deploymentId) {
        String tenantId = (String) Ft.ThreadLocal.get(Constants.TENANT_ID);
        Optional.ofNullable(tenantId).orElseThrow(() -> new BizException(RtCodeEnum.R10009));
        //会同时删除流程实例
        processDefinitionService.deleteDeployment(deploymentId, tenantId, true);
        return Rt.success("流程部署删除成功！");
    }

    /**
     * 挂起流程
     *
     * @param processDefId
     * @param
     * @return
     */
    @PostMapping("deployment/suspend")
    public Rt suspendDeployment(@NotBlank(message = "流程定义ID不能为空") @RequestParam String processDefId) {
        String tenantId = (String) Ft.ThreadLocal.get(Constants.TENANT_ID);
        Optional.ofNullable(tenantId).orElseThrow(() -> new BizException(RtCodeEnum.R10009));
        processDefinitionService.suspendProcessDefinition(processDefId, tenantId);
        return Rt.success("流程挂起成功！");
    }

    /**
     * 激活流程
     *
     * @param processDefId
     * @param
     * @return
     */
    @PostMapping("deployment/active")
    public Rt activeDeployment(@NotBlank(message = "流程定义ID不能为空") @RequestParam String processDefId) {
        String tenantId = (String) Ft.ThreadLocal.get(Constants.TENANT_ID);
        Optional.ofNullable(tenantId).orElseThrow(() -> new BizException(RtCodeEnum.R10009));
        processDefinitionService.activeProcessDefinition(processDefId, tenantId);
        return Rt.success("流程激活成功！");
    }

    /**
     * 查看部署定义流程图
     *
     * @param response
     * @param processDefId
     * @throws Exception
     */
    @GetMapping("getProcessDiagram")
    public void getProcessDiagram(HttpServletResponse response, @NotBlank(message = "流程定义ID不能为空") @RequestParam String processDefId) throws Exception {
        //使用宋体
        String fontName = "宋体";
        InputStream ins = null;
        OutputStream out = null;
        try {
            //获取BPMN模型对象
            BpmnModel model = repositoryService.getBpmnModel(processDefId);
            ins = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator()
                    .generateDiagram(model, "png", new ArrayList<String>(), new ArrayList<String>(), fontName, fontName, fontName, null, 1.0);

            response.setContentType("image/png");
            BufferedImage image = ImageIO.read(ins);
            out = response.getOutputStream();
            ImageIO.write(image, "png", out);
        } catch (Exception e) {
            logger.error("生成流程图失败", e);
            new BizException(RtCodeEnum.R_ERROR.getCode(), "流程图生成失败！");
        } finally {
            if (ins != null) {
                ins.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }

    /**
     * 查询最新的流程定义key
     *
     * @param
     * @return
     */
    @PostMapping("getProcDefKeys")
    public Rt getProcDefKeys() {
        String tenantId = (String) Ft.ThreadLocal.get(Constants.TENANT_ID);
        Optional.ofNullable(tenantId).orElseThrow(() -> new BizException(RtCodeEnum.R10009));
        Map<String, String> map = processDefinitionService.getProcDefKeys(tenantId);
        return Rt.success(map);
    }

    /**
     * 查询流程业务配置
     *
     * @param pageRequest
     * @return
     */
    @PostMapping("getProcessBizConfigPageList")
    public PageResponse getProcessBizConfigPageList(PageRequest<CfProcConfig> pageRequest) {
        String tenantId = (String) Ft.ThreadLocal.get(Constants.TENANT_ID);
        Optional.ofNullable(tenantId).orElseThrow(() -> new BizException(RtCodeEnum.R10009));
        pageRequest.getParam().setTenantId(tenantId);
        return processDefinitionService.getProcessBizConfigPageList(pageRequest);
    }

    /**
     * 保存流程业务配置
     *
     * @param config
     * @return
     */
    @PostMapping("bizConfig/save")
    public Rt saveBizConfig(@RequestBody CfProcConfig config) {
        String tenantId = (String) Ft.ThreadLocal.get(Constants.TENANT_ID);
        Optional.ofNullable(tenantId).orElseThrow(() -> new BizException(RtCodeEnum.R10009));
        config.setTenantId(tenantId);
        processDefinitionService.saveBizConfig(config);
        return Rt.success("保存成功！");
    }

    /**
     * 删除流程业务配置
     *
     * @param config
     * @return
     */
    @PostMapping("bizConfig/del")
    public Rt deleteBizConfig(@RequestBody CfProcConfig config) {
        String tenantId = (String) Ft.ThreadLocal.get(Constants.TENANT_ID);
        Optional.ofNullable(tenantId).orElseThrow(() -> new BizException(RtCodeEnum.R10009));
        config.setTenantId(tenantId);
        processDefinitionService.deleteBizConfig(config);
        return Rt.success("删除成功！");
    }

    /**
     * 查询最新流程定义列表
     *
     * @param pageRequest
     * @return
     */
    @PostMapping("getProcessDefinitionLatestVersionPageList")
    public PageResponse getProcessDefinitionLatestVersionPageList(PageRequest pageRequest) {
        String tenantId = (String) Ft.ThreadLocal.get(Constants.TENANT_ID);
        pageRequest.setParam(tenantId);
        PageResponse pageResponse = processDefinitionService.getProcessDefinitionLatestVersionPageList(pageRequest);
        return pageResponse;
    }

    /**
     * 查询流程任务节点的权限列表
     *
     * @param pageRequest
     * @return
     */
    @PostMapping("getProcessTaskIdentityPageList")
    public PageResponse getProcessTaskIdentityPageList(PageRequest<CfTaskPosition> pageRequest) {
        String tenantId = (String) Ft.ThreadLocal.get(Constants.TENANT_ID);
        pageRequest.getParam().setTenantId(tenantId);
        PageResponse pageResponse = processDefinitionService.getProcessTaskIdentityPageList(pageRequest);
        return pageResponse;
    }

    /**
     * 根据流程定义ID查询所有的人工节点
     *
     * @param processDefId
     * @return
     */
    @PostMapping("getAllUserTask")
    public Rt getAllUserTask(@NotBlank(message = "流程定义ID不能为空") @RequestParam String processDefId) {
        Map<String, String> userTasks = processDefinitionService.getAllUserTask(processDefId);
        return Rt.success(userTasks);
    }

    /**
     * 添加节点权限配置
     *
     * @param taskPosition
     * @return
     */
    @PostMapping("addTaskRole")
    public Rt addTaskRole(@Validated @RequestBody CfTaskPosition taskPosition) {
        processDefinitionService.addTaskRole(taskPosition);
        return Rt.success("添加成功！");
    }

    /**
     * 修改节点权限配置
     *
     * @param taskPosition
     * @return
     */
    @PostMapping("updateTaskRole")
    public Rt updateTaskRole(@Validated @RequestBody CfTaskPosition taskPosition) {
        processDefinitionService.updateTaskRole(taskPosition);
        return Rt.success("修改成功！");
    }

    /**
     * 查询所有的作业异常信息
     *
     * @param bizKey
     * @return
     */
    @PostMapping("getAllException")
    public PageResponse getAllException(@RequestParam String bizKey) {
        PageRequest<String> pageRequest = new PageRequest<>();
        PageResponse pageResponse = processManageService.getAllException(pageRequest);
        return pageResponse;
    }

    /**
     * 查询作业异常堆栈信息
     *
     * @param jobId
     * @return
     */
    @PostMapping("getExceptionTrace")
    public Rt getExceptionTrace(@NotBlank(message = "作业ID不能为空") @RequestParam String jobId) {
        String trace = processManageService.getExceptionTrace(jobId);
        return Rt.success(trace);
    }

    /**
     * 处理异常
     *
     * @param executionId
     * @return
     */
    @PostMapping("dealException")
    public Rt dealException(@NotBlank(message = "任务执行ID不能为空") @RequestParam String executionId) {
        processManageService.dealException(executionId);
        return Rt.success("执行成功！");
    }

    /**
     * 流程实例列表查询
     *
     * @param pageRequest
     * @return
     */
    @PostMapping("getProcessInstPageList")
    public PageResponse getProcessInstPageList(PageRequest<ProcInstance> pageRequest) {
        PageResponse pageResponse = processManageService.getProcessInstPageList(pageRequest);
        return pageResponse;
    }


    @PostMapping("createGroup")
    public Rt createGroup() {
        GroupEntityImpl groupEntity = new GroupEntityImpl();
        String id = String.valueOf(SnowflakeUtil.genId());
        groupEntity.setName("产品组");
        groupEntity.setType("");
        groupEntity.setId(id);
        groupEntity.setRevision(0);
        groupEntity.setInserted(false);
        groupEntity.setUpdated(false);
        groupEntity.setDeleted(false);
        identityService.saveGroup(groupEntity);

        Group group = identityService.createGroupQuery().groupId(id).singleResult();
        return Rt.success(group);
    }

    @PostMapping("task")
    public void task(@RequestParam String processInstanceId) {
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        task.setAssignee("1001");
        taskService.complete(task.getId());
    }


    @GetMapping("getDiagram")
    public void getDiagram(HttpServletResponse response, @RequestParam String processInstanceId) throws Exception {
        //获得流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        String processDefinitionId = StringUtils.EMPTY;
        if (processInstance == null) {
            //查询已经结束的流程实例
            HistoricProcessInstance processInstanceHistory =
                    historyService.createHistoricProcessInstanceQuery()
                            .processInstanceId(processInstanceId).singleResult();
            if (processInstanceHistory == null) {
                return;
            } else {
                processDefinitionId = processInstanceHistory.getProcessDefinitionId();
            }
        } else {
            processDefinitionId = processInstance.getProcessDefinitionId();
        }

        //使用宋体
        String fontName = "宋体";
        //获取BPMN模型对象
        BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
        //获取流程实例当前的节点，需要高亮显示
        List<String> currentActs = Collections.EMPTY_LIST;
        if (processInstance != null) {
            currentActs = runtimeService.getActiveActivityIds(processInstance.getId());
        }

        InputStream is = processEngine.getProcessEngineConfiguration()
                .getProcessDiagramGenerator()
                .generateDiagram(model, "png", currentActs, new ArrayList<String>(),
                        fontName, fontName, fontName, null, 1.0);
        if (is == null) {
            return;
        }

        response.setContentType("image/png");

        BufferedImage image = ImageIO.read(is);
        OutputStream out = response.getOutputStream();
        ImageIO.write(image, "png", out);

        is.close();
        out.close();
    }


    /**
     * 流程实例ID：5005
     * 流程实例InstanceId：5005
     * 流程实例名称：null
     * 流程定义key：my-process-demo
     * 流程定义名称：测试流程1
     * 活动ID：null
     * 业务编号：202209292114179089
     * 流程描述：null
     * 流程定义版本号：1
     * 参数变量：{}
     */
    @PostMapping("startProcess")
    public String startProcess(@RequestParam String processDefinitionKey) {
        String bizKey = DateFormatUtils.format(new Date(), "yyyyMMddHHmmss") + RandomUtils.getRandomNumber(4);
        String tenantId = (String) Ft.ThreadLocal.get(Constants.TENANT_ID);
        Map<String, String> map = new HashMap<>();
        map.put("assigness", "zhansan");
        String str = processManageService.startProcessByDefKey(bizKey, BizProcDefTypeEnum.PROCESS_CREDIT, new HashMap<>(), tenantId);
//        ProcessInstance processInstance = runtimeService.startProcessInstanceByKeyAndTenantId(processDefinitionKey, bizKey, tenantId);
//        System.out.println("流程实例ID：" + processInstance.getId());
//        System.out.println("流程实例InstanceId：" + processInstance.getProcessInstanceId());
//        System.out.println("流程实例名称：" + processInstance.getName());
//        System.out.println("流程定义key：" + processInstance.getProcessDefinitionKey());
//        System.out.println("流程定义名称：" + processInstance.getProcessDefinitionName());
//        System.out.println("活动ID：" + processInstance.getActivityId());
//        System.out.println("业务编号：" + processInstance.getBusinessKey());
//        System.out.println("流程描述：" + processInstance.getDescription());
//        System.out.println("流程定义版本号：" + processInstance.getProcessDefinitionVersion());
//        System.out.println("参数变量：" + processInstance.getProcessVariables());
//        return "流程实例ID：" + processInstance.getId() + "，流程实例InstanceId：" + processInstance.getProcessInstanceId();
        return "流程实例ID：" + str;
    }

    /**
     * 流程定义ID：my-process-demo:1:5004
     * 流程定义名称：测试流程1
     * 流程定义key：my-process-demo
     * 流程定义描述：第一个activiti测试工作流
     * 流程定义版本号：1
     * 流程定义DiagramResourceName：bpmn/my-process-demo.my-process-demo.png
     * 流程定义ResourceName：bpmn/my-process-demo.bpmn20.xml
     *
     * @param deploymentId
     * @return
     */
    @GetMapping("getProcessDefinition")
    public String getProcessDefinition(@RequestParam String deploymentId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        System.out.println("流程定义ID：" + processDefinition.getId());
        System.out.println("流程定义名称：" + processDefinition.getName());
        System.out.println("流程定义key：" + processDefinition.getKey());
        System.out.println("流程定义描述：" + processDefinition.getDescription());
        System.out.println("流程定义版本号：" + processDefinition.getVersion());
        System.out.println("流程定义DiagramResourceName：" + processDefinition.getDiagramResourceName());
        System.out.println("流程定义ResourceName：" + processDefinition.getResourceName());
        return "流程定义ID：" + processDefinition.getId();
    }
}
